package pe.com.ztaxi.taxis.service.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import pe.com.ztaxi.taxis.dao.UnidadMovilDAO;
import pe.com.ztaxi.taxis.model.OrdenServicio;
import pe.com.ztaxi.taxis.model.UnidadMovil;
import pe.com.ztaxi.taxis.service.PuntoService;
import pe.com.ztaxi.taxis.service.UnidadMovilService;
import pe.com.ztaxi.taxis.service.ZonaService;
import pe.com.ztaxi.taxis.util.MapsJsonGoogle;

@Service
public class UnidadMovilServiceImpl implements UnidadMovilService {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(UnidadMovilServiceImpl.class);

	@Resource
	private UnidadMovilDAO unidadMovilDAO;
	
	@Autowired
	private PuntoService puntoService;
	
	@Autowired
	private ZonaService zonaService;

	@Transactional(readOnly = true)
	public UnidadMovil buscarPorCodigo(Integer codigoUnidadMovil){
		return unidadMovilDAO.findOne(codigoUnidadMovil);
	}
	
	@Transactional(readOnly = true)
	public List<UnidadMovil> buscarUnidadesCercanas(OrdenServicio ordenServicio) {
		LOGGER.debug("Buscando todas las unidades moviles");
		List<UnidadMovil> unidadesTodas = unidadMovilDAO.buscarUnidadesDisponibles(ordenServicio.getTipoVehiculo(), ordenServicio.isMaleteraAmplia());
		List<UnidadMovil> unidadesCercanas = new ArrayList<UnidadMovil>();
		int radio = ordenServicio.getPasajero().getEmpresa().getParamRadio();
		do {			
			for(UnidadMovil unidad : unidadesTodas){
				double distancia = puntoService.calcularDistancia(unidad.getUbicacionActual(), ordenServicio.getPuntoOrigen());
				if(distancia <= radio){
					calcularLlegadaEsperada(unidad, ordenServicio);										
					unidadesCercanas.add(unidad);
				}
			}
			radio = radio + ordenServicio.getPasajero().getEmpresa().getParamRadioIncremental();
		} while (unidadesCercanas.isEmpty() && radio <= ordenServicio.getPasajero().getEmpresa().getParamRadioMaximo());		
		return unidadesCercanas;
	}
	
	@Transactional(readOnly = true)
	public List<UnidadMovil> buscarUnidadesCercanasPuntuales(OrdenServicio ordenServicio) {
		LOGGER.debug("Buscando todas las unidades moviles");
		List<UnidadMovil> unidadesTodas = unidadMovilDAO.buscarUnidadesDisponibles(ordenServicio.getTipoVehiculo(), ordenServicio.isMaleteraAmplia());
		List<UnidadMovil> unidadesCercanas = new ArrayList<UnidadMovil>();
		int radio = ordenServicio.getPasajero().getEmpresa().getParamRadio();
		do {			
			for(UnidadMovil unidad : unidadesTodas){
				double distancia = puntoService.calcularDistancia(unidad.getUbicacionActual(), ordenServicio.getPuntoOrigen());
				if(distancia <= radio){
					calcularLlegadaEsperada(unidad, ordenServicio);
					if(unidad.getHoraEstimadaLlegada().before(ordenServicio.getHoraServicio())){
						unidadesCercanas.add(unidad);
					}					
				}
			}
			radio = radio + ordenServicio.getPasajero().getEmpresa().getParamRadioIncremental();
		} while (unidadesCercanas.isEmpty() && radio <= ordenServicio.getPasajero().getEmpresa().getParamRadioMaximo());		
		return unidadesCercanas;
	}
	
	public UnidadMovil getUnidadMasAdecuada(List<UnidadMovil> unidades, OrdenServicio orden){
		//ordenar lista por margen de tiempo
		Collections.sort(unidades, new Comparator<UnidadMovil>(){
		    public int compare(UnidadMovil u1, UnidadMovil u2) {
		        return u2.getMargen().compareTo(u1.getMargen());
		    }
		});
		//devuelve unidad adecuada
		for(UnidadMovil unidad : unidades){
//			System.out.println("Diferencia: " + unidad.getHoraEstimadaLlegada().getTime() + " mayor a " + orden.getHoraServicio().getTime() + " mas " +  )
			if(unidad.getHoraEstimadaLlegada().getTime() > orden.getHoraServicio().getTime() - orden.getPasajero().getEmpresa().getParamMinutosAutoAsignacion()*60000){
				return unidad;
			}
		}
		return null;
	}
	
	public void calcularLlegadaEsperada(UnidadMovil unidad, OrdenServicio ordenServicio){
		RestTemplate restTemplate = new RestTemplate();
		MapsJsonGoogle j = restTemplate.getForObject("http://maps.googleapis.com/maps/api/distancematrix/json?origins=" + 
			unidad.getUbicacionActual().getLatitud() + "," + unidad.getUbicacionActual().getLongitud() + 
			"&destinations=" + ordenServicio.getPuntoOrigen().getLatitud() + "," + ordenServicio.getPuntoOrigen().getLongitud() + "&sensor=false" , MapsJsonGoogle.class);
		unidad.setRadio(j.getRows()[0].getElements()[0].getDistance().getValue() / 1000d);
		Calendar aux = Calendar.getInstance();
		aux.add(Calendar.SECOND, j.getRows()[0].getElements()[0].getDuration().getValue());
		unidad.setHoraEstimadaLlegada(aux.getTime());
		double minDif = ((ordenServicio.getHoraServicio().getTime() - unidad.getHoraEstimadaLlegada().getTime())/60000);
		unidad.setMargen(minDif);
	}	
	
	public UnidadMovil actualizarUbicacion(UnidadMovil unidadMovil){
		UnidadMovil actual = unidadMovilDAO.findOne(unidadMovil.getId());
		double latitud = unidadMovil.getUbicacionActual().getLatitud();
		double longitud = unidadMovil.getUbicacionActual().getLongitud();
		actual.getUbicacionActual().setLatitud(latitud);
		actual.getUbicacionActual().setLongitud(longitud);
		actual.getUbicacionActual().setZona(zonaService.ubicarZona(latitud,longitud));
		return unidadMovilDAO.save(actual);
	}

	@Transactional
	public void actualizarEstadoDestinoFinal(UnidadMovil unidadMovil) {
		unidadMovil.setSituacion("Hacia destino final");
		unidadMovilDAO.save(unidadMovil);
	}

	@Transactional
	public boolean deshacerDestinoFinal(UnidadMovil unidadMovil) {
		if(unidadMovil.getSituacion().equals("Hacia destino final")){
			unidadMovil.setSituacion("En proceso");
			unidadMovilDAO.save(unidadMovil);
			return true;
		} else {
			return false;
		}
	}
	
}
